/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.aplikator.server.persistence.empiredb.oracle;

// Imports
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.empire.commons.StringUtils;
import org.apache.empire.db.DBColumnExpr;
import org.apache.empire.db.DBDatabase;
import org.apache.empire.db.expr.column.DBAliasExpr;
import org.apache.empire.db.oracle.DBCommandOracle;
import org.apache.empire.exceptions.ObjectNotValidException;

/**
 * This class handles the special features of an oracle database.
 * 
 * 
 */
public class EmpireCommandOracle extends DBCommandOracle {
	private final static long serialVersionUID = 1L;

	// limit rows
	protected Integer limitRows = null;
	protected Integer skipRows = null;

	/**
	 * Constructs an oracle command object.
	 * 
	 * @see org.apache.empire.db.DBCommand
	 * 
	 * @param db
	 *            the oracle database object this command belongs to
	 */
	public EmpireCommandOracle(DBDatabase db) {
		super(db);
	}

	@Override
	public void clearLimit() {
		limitRows = null;
		skipRows = null;
	}

	/**
	 * Creates an Oracle specific select statement that supports special
	 * features of the Oracle DBMS like e.g. CONNECT BY PRIOR
	 * 
	 * @param buf
	 *            the SQL statement
	 */
	@Override
	public synchronized void getSelect(StringBuilder buf) {
		List<DBAliasExpr> aliases = new ArrayList<DBAliasExpr>();
		resetParamUsage();
		if (select == null)
			throw new ObjectNotValidException(this);

		// Prepares statement
		if ((limitRows != null) || (skipRows != null)) {
			// add aliases
			Iterator<DBColumnExpr> iter = select.iterator();
			int i = 0;
			while (iter.hasNext()) {
				i++;
				aliases.add(new DBAliasExpr(iter.next(), "alias" + i));
			}

			buf.append("SELECT * FROM (\r\n");
		}

		buf.append("SELECT ");
		if (StringUtils.isNotEmpty(optimizerHint)) { // Append an optimizer hint
														// to the select
														// statement e.g. SELECT
														// /*+ RULE */
			buf.append("/*+ ").append(optimizerHint).append(" */ ");
		}
		if (selectDistinct)
			buf.append("DISTINCT ");

		// Add Select Expressions
		if ((limitRows != null) || (skipRows != null)) {
			addListExpr(buf, aliases, CTX_ALL, ", ");
			buf.append(", row_number() over (");
			insertOrderBy(buf);
			buf.append(") as rownumber ");
		} else {
			addListExpr(buf, select, CTX_ALL, ", ");
		}

		addFrom(buf);
		// Where

		addWhere(buf);
		// Connect By
		if (connectBy != null) { // Add 'Connect By Prior' Expression
			buf.append("\r\nCONNECT BY PRIOR ");
			connectBy.addSQL(buf, CTX_DEFAULT | CTX_NOPARENTHESES);
			// Start With
			if (startWith != null) { // Add 'Start With' Expression
				buf.append("\r\nSTART WITH ");
				startWith.addSQL(buf, CTX_DEFAULT);
			}
		}
		// Grouping
		addGrouping(buf);
		// Order
		if ((skipRows == null) && (limitRows == null)) {
			insertOrderBy(buf);
		} else {
			buf.append(") WHERE (");
			if (skipRows != null) {
				buf.append("rownumber>" + skipRows.toString());
			}

			if (limitRows != null) {
				if (skipRows != null) {
					buf.append(" AND ");
				}
				Integer outputLimit = new Integer(limitRows.intValue());
				if (skipRows != null) {
					outputLimit += skipRows;
				}

				buf.append("rownumber<=" + outputLimit.toString());
			}
			buf.append("\r\n)");
		}
	}

	private void insertOrderBy(StringBuilder buf) {
		if (orderBy != null) { // Having
			if (connectBy != null)
				buf.append("\r\nORDER SIBLINGS BY ");
			else
				buf.append("\r\nORDER BY ");
			// Add List of Order By Expressions
			addListExpr(buf, orderBy, CTX_DEFAULT, ", ");
		}
	}

	@Override
	public void limitRows(int numRows) {
		limitRows = new Integer(numRows);
	}

	@Override
	public void skipRows(int numRows) {
		skipRows = new Integer(numRows);
	}
}